package test.demo.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @author Hinsteny
 * @date 2016/5/25
 * @copyright: 2016 All rights reserved.
 */
public class NioServer {

	private Charset charset = Charset.forName("UTF-8");
	
    //相当于自定义协议格式，与客户端协商好
    public static String BROAD_CAST_FLAG = "###";
    
	private Selector selector = null;
	private int port = 7777;

	public NioServer(int port) throws IOException {
		this.port = port;
		this.selector = Selector.open();
	}

	@SuppressWarnings("rawtypes")
	public void work() throws IOException {
		ServerSocketChannel server = ServerSocketChannel.open();
		server.bind(new InetSocketAddress(port));
		// 非阻塞的方式
		server.configureBlocking(false);
		// 注册到选择器上，设置为监听状态
		server.register(selector, SelectionKey.OP_ACCEPT);

		System.out.println("Server is listening now...");

		while (true) {
			int readyChannels = selector.select();
			if (readyChannels == 0)
				continue;
			Set selectedKeys = selector.selectedKeys(); // 可以通过这个方法，知道可用通道的集合
			Iterator keyIterator = selectedKeys.iterator();
			while (keyIterator.hasNext()) {
				SelectionKey sk = (SelectionKey) keyIterator.next();
				keyIterator.remove();
				dealWithConnection(server, sk);
			}
		}
	}

	/**
	 * @throws IOException
	 * 
	 */
	private void dealWithConnection(ServerSocketChannel server, SelectionKey sk) throws IOException {
		if (sk.isAcceptable()) {
			SocketChannel sc = server.accept();
			// 非阻塞模式
			sc.configureBlocking(false);
			// 注册选择器，并设置为读取模式，收到一个连接请求，然后起一个SocketChannel，并注册到selector上，之后这个连接的数据，就由这个SocketChannel处理
			sc.register(selector, SelectionKey.OP_READ);

			// 将此对应的channel设置为准备接受其他客户端请求
			sk.interestOps(SelectionKey.OP_ACCEPT);
			System.out.println("Server is listening from client :" + sc.getRemoteAddress());
			sc.write(charset.encode("Connection success!"));
		}
		// 处理来自客户端的数据读取请求
		if (sk.isReadable()) {
			// 返回该SelectionKey对应的 Channel，其中有数据需要读取
			SocketChannel sc = (SocketChannel) sk.channel();
			ByteBuffer buff = ByteBuffer.allocate(1024);
			StringBuilder content = new StringBuilder();
			try {
				while (sc.read(buff) > 0) {
					buff.flip();
					content.append(charset.decode(buff));
				}
				System.out.println("Server is listening from client " + sc.getRemoteAddress() + " data rev is: " + content);
				// 将此对应的channel设置为准备下一次接受数据
				sk.interestOps(SelectionKey.OP_READ);
			} catch (IOException io) {
				sk.cancel();
				if (sk.channel() != null) {
					sk.channel().close();
				}
			}
			// 广播消息
			if (content.length() > 0 && content.toString().startsWith(BROAD_CAST_FLAG)) {
				// 不回发给发送此内容的客户端
				BroadCast(selector, sc, content.toString());
			}
		}
	}

	public void BroadCast(Selector selector, SocketChannel except, String content) throws IOException {
		// 广播数据到所有的SocketChannel中
		for (SelectionKey key : selector.keys()) {
			Channel targetchannel = key.channel();
			// 如果except不为空，不回发给发送此内容的客户端
			if (targetchannel instanceof SocketChannel && targetchannel != except) {
				SocketChannel dest = (SocketChannel) targetchannel;
				dest.write(charset.encode(content));
			}
		}
	}

	public static void main(String[] args) {
		try {
			new NioServer(7777).work();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}
